﻿namespace MyTRCP.Common.Models
{
    public class DataPayload
    {
        public ushort PayloadType { get; set; }
        public ushort CommandId { get; set; }
        public byte[] Arguments { get; set; }
        public int ArgsLength { get; set; }
        public bool ChecksumFlag { get; set; }
        public DataPayload(ushort payloadType, ushort commandId, bool checksumFlag, params byte[] arguments)
        {
            PayloadType = payloadType;
            CommandId = commandId;
            Arguments = arguments;
            ArgsLength = arguments.Length;
            ChecksumFlag = checksumFlag;
        }
        public DataPayload(ushort payloadType, ushort commandId, bool checksumFlag, byte[] buf, int pos, int length)
        {
            PayloadType = payloadType;
            CommandId = commandId;
            ChecksumFlag = checksumFlag;
            Arguments = new byte[length];
            Array.Copy(buf, pos, Arguments, 0, length);
            ArgsLength = length;
        }
        public static byte[] Pack(DataPayload payload)
        {
            return Pack(payload.PayloadType, payload.CommandId, payload.Arguments, payload.ArgsLength, payload.ChecksumFlag);
        }
        public static DataPayload Unpack(byte[] payloadDatas)
        {
            if (payloadDatas == null)
            {
                throw new ArgumentNullException(nameof(payloadDatas), "载荷字节数组为空");
            }
            if (payloadDatas.Length < 8)
            {
                throw new ArgumentException("载荷长度小于8，不完整", nameof(payloadDatas));
            }
            if (payloadDatas[0] != 0xff)
            {
                throw new ArgumentException("幻数不正确", nameof(payloadDatas));
            }
            if (payloadDatas[1] != 0x01)
            {
                throw new ArgumentException("幻数不正确", nameof(payloadDatas));
            }
            bool checksumFlag = (payloadDatas[2] & 1) != 0;
            byte argsLen = payloadDatas[3];
            if (argsLen > 254)
            {
                throw new ArgumentException("参数长度过长", nameof(payloadDatas));
            }
            ushort payloadType = Tools.UInt16FromBEBA(payloadDatas, 4);
            ushort cmdId = Tools.UInt16FromBEBA(payloadDatas, 6);
            if (checksumFlag)
            {
                if (payloadDatas.Length < 9 + argsLen)
                {
                    throw new ArgumentException("载荷长度不足", nameof(payloadDatas));
                }
                byte checksum = 0;
                for (int i = 0; i < 8 + argsLen; i++)
                {
                    checksum = (byte)(checksum ^ payloadDatas[i]);
                }
                if (payloadDatas[8 + argsLen] != checksum)
                {
                    throw new ArgumentException("载荷checksum校验失败", nameof(payloadDatas));
                }
            }
            else if (payloadDatas.Length < 8 + argsLen)
            {
                throw new ArgumentException("载荷长度不足", nameof(payloadDatas));
            }
            DataPayload pl = new DataPayload(payloadType, cmdId, checksumFlag, payloadDatas, 8, argsLen);
            return pl;
        }
        public static byte[] Pack(int payloadType, int cmdId, byte[] argsBuf, int argsLen, bool checksumFlag)
        {
            if (argsBuf.Length < argsLen)
            {
                throw new ArgumentException("指定参数长度超过缓冲区长度", nameof(argsLen));
            }
            if (argsLen > 254)
            {
                throw new ArgumentException("参数长度过长", nameof(argsLen));
            }
            int checksumLen = checksumFlag ? 1 : 0;
            int payloadLen = 8 + argsLen + checksumLen;
            byte[] payload = new byte[payloadLen];
            payload[0] = 0xff;
            payload[1] = 0x01;
            payload[2] = (byte)(checksumFlag ? 1 : 0);
            payload[3] = (byte)argsLen;
            payload[4] = (byte)(payloadType >> 8);
            payload[5] = (byte)payloadType;
            payload[6] = (byte)(cmdId >> 8);
            payload[7] = (byte)cmdId;
            for (int i = 0; i < argsLen; i++)
            {
                payload[8 + i] = argsBuf[i];
            }
            if (checksumLen != 0)
            {
                int checksumPos = payloadLen - 1;
                payload[checksumPos] = Tools.ComputeChecksum(payload, 0, checksumPos);
            }
            return payload;
        }
    }
}